package client

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"io/ioutil"
	"openzbox.com/iot"
	"openzbox.com/log"
	"strings"
)

type IoTOpts struct {
	Type     iot.IOTS_TYPE
	Host     string
	Port     int
	Username string
	Password string
	Desc     string
	ClientID string
}

type MqttClient struct {
	conn mqtt.Client
	opts IoTOpts
}

//var mqttclient MqttClient

func (mc *MqttClient) Type() string {
	return "mqtt"
}

func (mc *MqttClient) Publish(topic string, msg string) {
	/*QoS 0：消息最多传递一次，如果当时客户端不可用，则会丢失该消息。
	QoS 1：消息传递至少 1 次。
	QoS 2：消息仅传送一次。*/
	mc.conn.Publish(topic, 0, false, msg)
}

func (mc *MqttClient) Subscribe(topic string, handleFun func([]byte)) error {
	/*
		1.重新连接MQTT服务时，不需要接收该主题最新消息，设置retained为false;
			2.重新连接MQTT服务时，需要接收该主题最新消息，设置retained为true;
		token := mc.conn.Subscribe(topic, 1, nil)
		token.Wait()
	*/
	token := mc.conn.Subscribe(topic, 1, func(c mqtt.Client, msg mqtt.Message) {
		//fmt.Printf("Receive Subscribe Message :")
		//fmt.Printf("Sub Client Topic : %s, Data size is  %d \n", msg.Topic(), len(msg.Payload()))
		if len(msg.Payload()) > 0 {
			handleFun(msg.Payload())
		}
	})
	if token.Error() != nil {
		//fmt.Println(token.Error())
		return token.Error()
	}
	token.Wait()
	return nil
}

func NewMqttClient(iotopts IoTOpts) (*MqttClient, error) {
	mqttclient := &MqttClient{}
	server := fmt.Sprintf("tcp://%s:%d", iotopts.Host, iotopts.Port)
	opts := mqtt.NewClientOptions().AddBroker(server).SetClientID(iotopts.ClientID)
	if strings.EqualFold(strings.TrimSpace(iotopts.Username), "") {
		opts.SetUsername(iotopts.Username)
	}
	if strings.EqualFold(strings.TrimSpace(iotopts.Password), "") {
		opts.SetPassword(iotopts.Password)
	}
	opts.SetDefaultPublishHandler(messagePubHandler)
	opts.OnConnect = connectHandler
	opts.OnConnectionLost = connectLostHandler
	opts.OnReconnecting = reconnectHandler
	//配置TLS 连接
	//tlsConfig := NewTlsConfig()
	//opts.SetTLSConfig(tlsConfig)
	c := mqtt.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		log.Error(token.Error())
		return nil, token.Error()
	}
	mqttclient.conn = c
	return mqttclient, nil
}

var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
	opt := client.OptionsReader()
	log.Infof("mqtt %s Connected", opt.Servers()[:])
}

var reconnectHandler mqtt.ReconnectHandler = func(client mqtt.Client, opts *mqtt.ClientOptions) {
	opt := client.OptionsReader()
	log.Infof("mqtt %s reconnected", opt.Servers()[:])
}

var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
	opt := client.OptionsReader()
	log.Infof("mqtt %s Connect lost", opt.Servers()[:])
}
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	//fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
	opt := client.OptionsReader()
	log.Infof("mqtt %s publish message: %s from topic: %s\n", opt.Servers()[:], msg.Payload(), msg.Topic())
}

func NewTlsConfig() *tls.Config {
	certpool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("ca.pem")
	if err != nil {
		log.Error(err.Error())
	}
	certpool.AppendCertsFromPEM(ca)
	return &tls.Config{
		RootCAs: certpool,
	}
}

func NewTlsConfigWithClient() *tls.Config {
	certpool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("ca.pem")
	if err != nil {
		log.Fatalln(err.Error())
	}
	certpool.AppendCertsFromPEM(ca)
	// Import client certificate/key pair
	clientKeyPair, err := tls.LoadX509KeyPair("client-crt.pem", "client-key.pem")
	if err != nil {
		panic(err)
	}
	return &tls.Config{
		RootCAs:            certpool,
		ClientAuth:         tls.NoClientCert,
		ClientCAs:          nil,
		InsecureSkipVerify: true,
		Certificates:       []tls.Certificate{clientKeyPair},
	}
}
